package xxl.ioc.typedescriptions 
{
	import flash.utils.Dictionary;
	import flash.utils.getQualifiedClassName;
	/**
	 * ...
	 * @author XXL
	 */
	public class TypeDescription 
	{
		public var ctor:ConstructorInjectionPoint;
		public var injectionPoints:InjectionPoint;
		public var preDestoryMethods:PreDestroyInjectionPoint;
		
		private var _postConstructAdded:Boolean;
		
		public function TypeDescription(useDefaultConstructor:Boolean = true) 
		{
			if (useDefaultConstructor)
			{
				ctor = new NoParamsConstructorInjectionPoint();
			}
		}
		
		public function setConstructor(parameterTypes:Array, parameterNames:Array = null, requiredParameters:int = int.MAX_VALUE, metadata:Dictionary = null):TypeDescription
		{
			ctor = new ConstructorInjectionPoint(
				createParameterMappings(parameterTypes, parameterNames || [])
				, requiredParameters
				, metadata);
			return this;
		}
		
		public function addFieldInjection(fieldName:String, type:Class, injectionName:String = "", optional:Boolean = false, metadata:Dictionary = null):TypeDescription
		{
			if (_postConstructAdded)
			{
				throw new Error("不能在添加完PostConstruct方法之后再添加新的Inject");
			}
			addInjectionPoint(new PropertyInjectionPoint(getQualifiedClassName(type) + "|" + injectionName, fieldName, optional, metadata));
			return this;
		}
		
		public function addMethodInjection(methodName:String, parameterTypes:Array, parameterNames:Array = null, requiredParameters:int = int.MAX_VALUE, optional:Boolean = false, metadata:Dictionary = null):TypeDescription
		{
			if (_postConstructAdded)
			{
				throw new Error("不能在添加完PostConstruct方法之后再添加新的Inject");
			}
			addInjectionPoint(new MethodInjectionPoint(
				methodName
				, createParameterMappings(parameterTypes, parameterNames || [])
				, Math.min(requiredParameters, parameterTypes.length)
				, optional
				, metadata));
			return this;
		}
		
		public function addPostConstructMethod(methodName:String, parameterTypes:Array, parameterNames:Array = null, requiredParameters:int = int.MAX_VALUE):TypeDescription
		{
			_postConstructAdded = true;
			addInjectionPoint(new PostConstructInjectionPoint(
				methodName
				, createParameterMappings(parameterTypes, parameterNames || [])
				, Math.min(requiredParameters, parameterTypes.length)
				, 0));
			return this;
		}
		
		public function addPreDestroyMethod(methodName:String, parameterTypes:Array, parameterNames:Array = null, requiredParameters:int = int.MAX_VALUE):TypeDescription
		{
			const method:PreDestroyInjectionPoint = new PreDestroyInjectionPoint(
				methodName
				, createParameterMappings(parameterTypes, parameterNames || [])
				, Math.min(requiredParameters, parameterTypes.length)
				, 0);
			if (preDestoryMethods)
			{
				preDestoryMethods.last.next = method;
				preDestoryMethods.last = method;
			}
			else
			{
				preDestoryMethods = method;
				preDestoryMethods.last = method;
			}
			return this;
		}
		
		public function addInjectionPoint(injectionPoint:InjectionPoint):void
		{
			if (injectionPoints)
			{
				injectionPoints.last.next = injectionPoint;
				injectionPoints.last = injectionPoint;
			}
			else
			{
				injectionPoints = injectionPoint;
				injectionPoints.last = injectionPoint;
			}
		}
		
		//----------private
		
		private function createParameterMappings(parameterTypes:Array, parameterNames:Array):Array
		{
			const parameters:Array = new Array(parameterTypes.length);
			for (var i:int = parameters.length; i--; )
			{
				parameters[i] = getQualifiedClassName(parameterTypes[i]) + "|" + (parameterNames[i] || "");
			}
			return parameters;
		}
		
	}

}